# Copyright (C) 2009 Frank Ronny Larsen.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from datetime import datetime

import gobject

from scapy import *


class HTTP_Object(object):
    def __init__(self, pkt):
        self.pkts = []
        self.is_response = True
        
        self.time = datetime.fromtimestamp(pkt.time)
        self.timestr = "%s.%03d" % (self.time.strftime("%H:%M:%S"), self.time.microsecond/1000)

        self.lines = []
        self.line = "not parsed"
        self.body = ""
        self.headers = dict()
        self.method = "Response"
        self.url = ""
        self.version = ""
        self.rsp_code = ""
        self.rsp_text = ""
        self.size = 0

        self.append(pkt)

    def parse(self):
        req = self.get_raw()

        # Sometimes the chat starts with a packet without any data.
        if not req:
            return

        self.lines = req.splitlines()
        self.line = self.lines[0]
        if self.line.startswith("HTTP"):
            comps = self.lines[0].split(" ", 2)
            self.is_response = False
            self.method = comps[0]
            self.rsp_code = comps[1]
            self.rsp_text = comps[2]
        else:
            comps = self.lines[0].split(" ", 2)
            self.method = comps[0]
            self.url = comps[1]
            self.version = comps[2]

        # Get header lines. Headers end on first empty line. All lines
        # after that is body.
        bodylines = []
        doingheaders = True
        for line in self.lines[1:]:
            if not line:
                doingheaders = False
                continue
            if doingheaders:
                name, content = line.split(':',1)
                self.headers[name.strip()] = content.strip()
            else:
                bodylines.append(line)

        self.body = "\n".join(bodylines)

        print self.headers
    
    def get_raw(self):
        text = "".join([pkt[Raw].load for pkt in self.pkts if Raw in pkt])
        return text
    
    def append(self, pkt):
        self.pkts.append(pkt)
        self.parse()


class HTTP_Transaction(object):
    def __init__(self, pkt=None):
        self.request = None
        self.response = None

        if pkt:
            self.append_request(pkt)

    def append_request(self, pkt):
        if not self.request:
            self.request = HTTP_Object(pkt)
        else:
            self.request.append(pkt)

    def append_response(self, pkt):
        if not self.response:
            self.response = HTTP_Object(pkt)
        else:
            self.response.append(pkt)


    @property
    def method(self):
        if not self.request:
            return None

        return self.request.method

    @property
    def time_string(self):
        if not self.request:
            return None
        return self.request.timestr

    @property
    def url(self):
        if not self.request:
            return None
        host = self.request.headers.get("Host", self.request.pkts[0][IP].dst)
        return "http://" + host + self.request.url

    @property
    def result(self):
        if not self.response:
            return ""
        return self.response.rsp_code

    @property
    def size(self):
        if not self.response:
            return 0
        return self.response.size

    @property
    def ctype(self):
        if self.response and "Content-Type" in self.response.headers:
            return self.response.headers["Content-Type"]
        return ""
    

class HTTP_Chat(object):
    def __init__(self, pkt, store=None):
        super(HTTP_Chat, self).__init__()

        self.src = "%s:%s" % (pkt[IP].src, pkt[TCP].sport)
        self.dst = "%s:%s" % (pkt[IP].dst, pkt[TCP].dport)
    
        # Create hash of sorted src-dst
        self.hash = hash("-".join(sorted([self.src, self.dst])))

        self.liststore = store

        obj = HTTP_Transaction(pkt)
        self.current_object = 0
        self.objects = [obj]

        gobject.idle_add(self.liststore.append_packet, self, 0)

    def __hash__(self):
        return self.hash

    def append(self, pkt, src, dst):
        print "Appending %s->%s" % (src, dst)
        obj = self.objects[self.current_object]
        if src == self.src:
            if Raw in pkt:
                lines = pkt[Raw].load.splitlines()
                if "HTTP" in lines[0]:
                    obj = HTTP_Transaction(pkt)
                    self.objects.append(obj)
                    self.current_object += 1
                    gobject.idle_add(self.liststore.append_packet, self, self.current_object)
                else:
                    obj.append_request(pkt)
                    gobject.idle_add(self.liststore.update_packet, obj)

        else:
            obj.append_response(pkt)
            gobject.idle_add(self.liststore.update_packet, obj)

